package org.finesys.common.netty.websocket.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.cors.CorsHandler;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.finesys.common.netty.websocket.support.WebSocketEndpointConfig;
import org.finesys.common.netty.websocket.support.WebSocketEndpointEventServer;
import org.finesys.common.netty.websocket.support.WsPathMatcher;
import org.springframework.beans.TypeMismatchException;
import org.springframework.util.ObjectUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Set;

import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static org.finesys.common.netty.websocket.constants.WebConfigConstant.MAX_FRAME_SIZE;

/**
 * 自定义处理协议内容
 */
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    // 初始化内部日志记录器
    private static final InternalLogger INTERNAL_LOGGER = InternalLoggerFactory.getInstance(HttpServerHandler.class);

    // WebSocket事件服务器
    private final WebSocketEndpointEventServer webSocketEndpointEventServer;

    // WebSocket端点配置
    private final WebSocketEndpointConfig webSocketEndpointConfig;

    // 事件执行器组
    private final EventExecutorGroup eventExecutorGroup;

    // 是否启用CORS
    private final boolean isCors;

    // 用于存储favicon的ByteBuf
    private static ByteBuf faviconByteBuf = null;

    // 用于存储404 Not Found响应的ByteBuf
    private static ByteBuf notFoundByteBuf = null;

    // 用于存储400 Bad Request响应的ByteBuf
    private static ByteBuf badRequestByteBuf = null;

    // 用于存储403 Forbidden响应的ByteBuf
    private static ByteBuf forbiddenByteBuf = null;

    // 用于存储500 Internal Server Error响应的ByteBuf
    private static ByteBuf internalServerErrorByteBuf = null;

    static {
        // 加载favicon图标
        faviconByteBuf = buildStaticRes("/favicon.ico");
        // 加载404页面
        notFoundByteBuf = buildStaticRes("/public/error/404.html");
        // 如果404页面加载失败，则加载备用4xx页面
        if (notFoundByteBuf == null) {
            notFoundByteBuf = buildStaticRes("/public/error/4xx.html");
        }
        // 加载400错误页面
        badRequestByteBuf = buildStaticRes("/public/error/400.html");
        // 如果400页面加载失败，则加载备用4xx页面
        if (badRequestByteBuf == null) {
            badRequestByteBuf = buildStaticRes("/public/error/4xx.html");
        }

        // 加载403错误页面
        forbiddenByteBuf = buildStaticRes("/public/error/403.html");
        // 如果403页面加载失败，则加载备用4xx页面
        if (forbiddenByteBuf == null) {
            forbiddenByteBuf = buildStaticRes("/public/error/4xx.html");
        }

        // 加载500内部服务器错误页面
        internalServerErrorByteBuf = buildStaticRes("/public/error/500.html");
        // 如果500页面加载失败，则加载备用5xx页面
        if (internalServerErrorByteBuf == null) {
            internalServerErrorByteBuf = buildStaticRes("/public/error/5xx.html");
        }
    }


    public HttpServerHandler(WebSocketEndpointEventServer webSocketEndpointEventServer, WebSocketEndpointConfig webSocketEndpointConfig, EventExecutorGroup eventExecutorGroup, boolean isCors) {
        this.webSocketEndpointEventServer = webSocketEndpointEventServer;
        this.webSocketEndpointConfig = webSocketEndpointConfig;
        this.eventExecutorGroup = eventExecutorGroup;
        this.isCors = isCors;
    }

    /**
     * 根据资源路径构建静态资源响应的ByteBuf对象
     *
     * @param resPath 资源路径，相对于类路径的位置
     * @return 包含静态资源内容的ByteBuf对象，如果资源不存在或读取失败则返回null
     */
    private static ByteBuf buildStaticRes(String resPath) {
        // 尝试打开资源输入流，并自动管理资源关闭
        try (InputStream inputStream = HttpServerHandler.class.getResourceAsStream(resPath)) {
            // 如果资源输入流为空，则返回null
            if (inputStream == null) {
                return null;
            }

            // 创建字节数组输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            // 创建缓冲区
            byte[] buffer = new byte[1024];
            int bytesRead;
            // 循环读取资源内容到缓冲区，直到读取完毕
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                // 将缓冲区内容写入到字节数组输出流中
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }

            // 获取字节数组输出流中的字节数组
            byte[] bytes = byteArrayOutputStream.toByteArray();
            // 创建ByteBuf对象并写入字节数组
            return ByteBufAllocator.DEFAULT.buffer(bytes.length).writeBytes(bytes);
        } catch (IOException e) {
            // 捕获IO异常并记录日志
            INTERNAL_LOGGER.error("Error reading or closing resource from path: {}", resPath, e);
        }
        // 如果出现异常或资源不存在，则返回null
        return null;
    }

    /**
     * 处理异常
     * <p>
     * 当在ChannelPipeline中处理数据时发生异常时，此方法会被调用。
     *
     * @param ctx   ChannelHandlerContext，提供了对Channel、ChannelPipeline及其操作的访问权限
     * @param cause Throwable，异常对象，表示在ChannelPipeline中处理数据时发生的异常
     * @throws Exception 如果处理异常时发生新的异常，则抛出该异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        INTERNAL_LOGGER.error("Exception caught in ChannelPipeline", cause);
        webSocketEndpointEventServer.doOnError(ctx.channel(), cause);
        ctx.close(); // 确保在异常发生后关闭连接
    }


    /**
     * 关闭非活动连接
     *
     * @param ctx 通道处理器上下文
     * @throws Exception 可能抛出异常
     */

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 调用WebSocket端点事件服务器的doOnClose方法，通知WebSocket连接已关闭
        webSocketEndpointEventServer.doOnClose(ctx.channel());
        // 调用父类的channelInactive方法，继续处理通道关闭事件
        super.channelInactive(ctx);
    }


    /**
     * 接收消息
     *
     * @param channelHandlerContext ChannelHandlerContext
     * @param request               FullHttpRequest
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest request) throws Exception {
        // 初始化FullHttpResponse对象为null
        FullHttpResponse fullHttpResponse = null;
        try {
            // 处理HTTP请求
            handleHttpRequest(channelHandlerContext, request);
        } catch (TypeMismatchException e) {
            // 捕获类型不匹配异常，记录错误日志
            INTERNAL_LOGGER.error("Type mismatch exception caught", e);
            // 构建错误响应
            fullHttpResponse = buildErrorResponse(HTTP_1_1, BAD_REQUEST, badRequestByteBuf);
        } catch (Exception e) {
            // 捕获其他异常，记录错误日志
            INTERNAL_LOGGER.error("Exception caught", e);
            // 构建错误响应
            fullHttpResponse = buildErrorResponse(HTTP_1_1, INTERNAL_SERVER_ERROR, internalServerErrorByteBuf);
        } finally {
            // 无论是否发生异常，都执行以下代码块
            if (fullHttpResponse != null) {
                // 发送HTTP响应
                sendHttpResponse(channelHandlerContext, request, fullHttpResponse);
            }
        }
    }


    /**
     * 构建错误响应
     *
     * @param version      HTTP版本
     * @param status       HTTP状态码
     * @param errorContent 错误内容ByteBuf
     * @return 构建好的FullHttpResponse
     */
    private FullHttpResponse buildErrorResponse(HttpVersion version, HttpResponseStatus status, ByteBuf errorContent) {
        // 创建一个新的FullHttpResponse对象，设置HTTP版本和状态码
        FullHttpResponse response = new DefaultFullHttpResponse(version, status);

        // 如果错误内容不为空
        if (errorContent != null) {
            // 将错误内容的副本写入到响应的内容中
            response.content().writeBytes(errorContent.retainedDuplicate());

            // 释放原始的错误内容ByteBuf，避免内存泄漏
            errorContent.release(); // 确保原ByteBuf释放
        }

        // 返回构建好的FullHttpResponse对象
        return response;
    }


    /**
     * 处理HTTP请求
     *
     * @param context ChannelHandlerContext，提供了对Channel、ChannelPipeline及其操作的访问权限
     * @param request FullHttpRequest，表示一个完整的HTTP请求
     */
    private void handleHttpRequest(ChannelHandlerContext context, FullHttpRequest request) {
        // 如果请求解码失败，发送错误响应
        if (!request.decoderResult().isSuccess()) {
            sendErrorResponse(context, request, BAD_REQUEST, badRequestByteBuf);
            return;
        }

        // 如果请求方法不是GET，发送错误响应
        if (!Objects.equals(request.method(), GET)) {
            sendErrorResponse(context, request, FORBIDDEN, forbiddenByteBuf);
            return;
        }

        // 获取请求头中的HOST字段
        HttpHeaders httpHeaders = request.headers();
        String host = httpHeaders.get(HttpHeaderNames.HOST);

        // 如果HOST为空或不允许该HOST访问，发送错误响应
        if (ObjectUtils.isEmpty(host) || !isHostAllowed(host)) {
            sendErrorResponse(context, request, FORBIDDEN, forbiddenByteBuf);
            return;
        }

        // 解析请求URI，获取路径
        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
        String path = queryStringDecoder.path();

        // 如果是请求网站图标，发送响应
        if ("/favicon.ico".equals(path)) {
            sendResponse(context, request, OK, faviconByteBuf);
            return;
        }

        // 获取路径匹配模式
        String pattern = getPathMatcherPattern(context, queryStringDecoder);
        if (pattern == null) {
            sendErrorResponse(context, request, NOT_FOUND, notFoundByteBuf);
            return;
        }

        // 如果不是WebSocket请求，发送错误响应
        if (!isWebSocketRequest(request)) {
            sendErrorResponse(context, request, FORBIDDEN, forbiddenByteBuf);
            return;
        }

        // 处理握手前的回调
        String subprotocols = handleBeforeHandshake(context, request, pattern);

        // 处理WebSocket握手
        handleWebSocketHandshake(context, request, subprotocols, pattern);
    }

    /**
     * 判断指定的主机是否被允许访问WebSocket服务
     *
     * @param host 主机地址
     * @return 如果主机被允许访问则返回true，否则返回false
     */
    private boolean isHostAllowed(String host) {
        String serverHost = webSocketEndpointEventServer.getHost();
        return ObjectUtils.isEmpty(serverHost) || "0.0.0.0".equals(serverHost) || serverHost.equals(host.split(":")[0]);
    }

    /**
     * 获取与WebSocket路径匹配器对应的路径模式
     *
     * @param context            通道处理器上下文
     * @param queryStringDecoder 查询字符串解码器
     * @return 匹配到的路径模式，如果没有匹配到则返回null
     */
    private String getPathMatcherPattern(ChannelHandlerContext context, QueryStringDecoder queryStringDecoder) {
        // 获取通道处理器上下文中的通道
        Channel channel = context.channel();

        // 获取WebSocket端点事件服务器中的所有路径匹配器
        Set<WsPathMatcher> pathMatcherSet = webSocketEndpointEventServer.getPathMatchers();

        // 遍历所有路径匹配器
        for (WsPathMatcher wsPathMatcher : pathMatcherSet) {
            // 判断路径匹配器是否匹配并提取查询字符串解码器和通道
            if (wsPathMatcher.matchAndExtract(queryStringDecoder, channel)) {
                // 返回匹配到的路径模式
                return wsPathMatcher.getPattern();
            }
        }

        // 如果没有匹配到任何路径模式，则返回null
        return null;
    }

    /**
     * 判断请求是否为WebSocket请求
     *
     * @param request 请求对象
     * @return 如果是WebSocket请求则返回true，否则返回false
     */
    private boolean isWebSocketRequest(FullHttpRequest request) {
        // 获取请求头
        HttpHeaders headers = request.headers();
        // 判断请求头中的Upgrade字段是否为"websocket"
        // 并且请求头中的Connection字段是否为"upgrade"
        return "websocket".equalsIgnoreCase(headers.get(UPGRADE))
                && "upgrade".equalsIgnoreCase(headers.get(CONNECTION)) && headers.contains(SEC_WEBSOCKET_VERSION)
                && headers.contains(SEC_WEBSOCKET_KEY);
    }


    /**
     * 在WebSocket握手之前进行处理
     *
     * @param context 通道处理器上下文
     * @param request 完整的HTTP请求
     * @param pattern WebSocket路径模式
     * @return WebSocket子协议，如果没有则为null
     */
    private String handleBeforeHandshake(ChannelHandlerContext context, FullHttpRequest request, String pattern) {
        String subprotocols = null;

        // 检查是否有WebSocket握手前的处理事件
        if (webSocketEndpointEventServer.hasBeforeHandshake(context.channel(), pattern)) {
            // 执行WebSocket握手前的处理事件
            webSocketEndpointEventServer.doBeforeHandshake(context.channel(), request, pattern);

            // 检查通道是否仍然活跃
            if (!context.channel().isActive()) {
                return null;
            }

            // 定义WebSocket子协议属性键
            AttributeKey<String> subprotocolsKey = AttributeKey.valueOf("subprotocols");

            // 检查通道是否有子协议属性
            if (context.channel().hasAttr(subprotocolsKey)) {
                // 获取子协议值
                subprotocols = context.channel().attr(subprotocolsKey).get();
            }
        }

        // 返回子协议值，如果没有则为null
        return subprotocols;
    }


    /**
     * 处理WebSocket握手请求
     *
     * @param context      通道处理器上下文
     * @param request      完整的HTTP请求
     * @param subprotocols WebSocket子协议
     * @param pattern      WebSocket路径模式
     */
    private void handleWebSocketHandshake(ChannelHandlerContext context, FullHttpRequest request, String subprotocols, String pattern) {
        // 创建WebSocketServerHandshakerFactory实例
        WebSocketServerHandshakerFactory webSocketServerHandshakerFactory =
                new WebSocketServerHandshakerFactory(getWebSocketLocation(request), subprotocols, true, webSocketEndpointConfig.getMaxFramePayloadLength());
        // 创建WebSocketServerHandshaker实例
        WebSocketServerHandshaker webSocketServerHandshaker = webSocketServerHandshakerFactory.newHandshaker(request);
        // 如果WebSocketServerHandshaker为null，则发送不支持的版本响应并返回
        if (webSocketServerHandshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(context.channel());
            return;
        }

        // 获取通道处理器管道
        ChannelPipeline channelPipeline = context.pipeline();
        // 从管道中移除当前上下文的处理器
        channelPipeline.remove(context.name());

        // 添加管道处理器
        addPipelineHandlers(channelPipeline);

        // 执行握手操作并添加监听器
        webSocketServerHandshaker.handshake(context.channel(), request).addListener(future -> {
            // 如果握手成功
            if (future.isSuccess()) {
                // 如果启用了CORS，则移除CorsHandler
                if (isCors) {
                    channelPipeline.remove(CorsHandler.class);
                }
                // 调用WebSocket端点事件服务器的doOnOpen方法
                webSocketEndpointEventServer.doOnOpen(context.channel(), request, pattern);
            } else {
                // 如果握手失败，则关闭通道并发送关闭帧
                webSocketServerHandshaker.close(context.channel(), new CloseWebSocketFrame());
            }
        });
    }


    /**
     * 向ChannelPipeline中添加处理器
     * <p>
     * 根据webSocketEndpointConfig的配置，向给定的ChannelPipeline中添加适当的处理器。
     *
     * @param channelPipeline ChannelPipeline，Netty的管道，用于处理网络事件
     */
    private void addPipelineHandlers(ChannelPipeline channelPipeline) {
        // 如果配置了空闲超时时间，则添加IdleStateHandler
        if (webSocketEndpointConfig.getReaderIdleTimeSeconds() != 0 ||
                webSocketEndpointConfig.getWriterIdleTimeSeconds() != 0 ||
                webSocketEndpointConfig.getAllIdleTimeSeconds() != 0) {
            // 添加IdleStateHandler以处理空闲超时
            channelPipeline.addLast(new IdleStateHandler(
                    webSocketEndpointConfig.getReaderIdleTimeSeconds(),
                    webSocketEndpointConfig.getWriterIdleTimeSeconds(),
                    webSocketEndpointConfig.getAllIdleTimeSeconds()
            ));
        }

        // 如果配置了使用压缩处理器，则添加WebSocketServerCompressionHandler
        if (webSocketEndpointConfig.isUseCompressionHandler()) {
            // 添加WebSocketServerCompressionHandler以处理WebSocket压缩
            channelPipeline.addLast(new WebSocketServerCompressionHandler());
        }

        // 添加WebSocketFrameAggregator以聚合WebSocket帧
        channelPipeline.addLast(new WebSocketFrameAggregator(MAX_FRAME_SIZE.getValue()));

        // 根据是否配置了使用EventExecutorGroup，向ChannelPipeline中添加WebSocketServerHandler
        if (webSocketEndpointConfig.isUseEventExecutorGroup()) {
            // 如果配置了使用EventExecutorGroup，则使用eventExecutorGroup作为参数添加WebSocketServerHandler
            channelPipeline.addLast(eventExecutorGroup, new WebSocketServerHandler(webSocketEndpointEventServer));
        } else {
            // 如果没有配置使用EventExecutorGroup，则直接添加WebSocketServerHandler
            channelPipeline.addLast(new WebSocketServerHandler(webSocketEndpointEventServer));
        }
    }


    /**
     * 发送错误响应
     *
     * @param context ChannelHandlerContext，提供了对Channel、ChannelPipeline及其操作的访问权限
     * @param request FullHttpRequest，表示一个完整的HTTP请求
     * @param status  HttpResponseStatus，表示HTTP响应的状态码
     * @param byteBuf ByteBuf，包含要发送的响应内容，可能为null
     *                <p>
     *                如果byteBuf不为null，则创建一个包含byteBuf内容的FullHttpResponse对象，
     *                否则创建一个仅包含状态码的FullHttpResponse对象。
     *                最后，调用sendHttpResponse方法发送响应。
     */
    private void sendErrorResponse(ChannelHandlerContext context, FullHttpRequest request, HttpResponseStatus status, ByteBuf byteBuf) {
        FullHttpResponse response = byteBuf != null
                ? new DefaultFullHttpResponse(HTTP_1_1, status, byteBuf.retainedDuplicate())
                : new DefaultFullHttpResponse(HTTP_1_1, status);
        sendHttpResponse(context, request, response);
    }

    /**
     * 发送响应
     *
     * @param context ChannelHandlerContext，提供了对Channel、ChannelPipeline及其操作的访问权限
     * @param request FullHttpRequest，表示一个完整的HTTP请求
     * @param status  HttpResponseStatus，表示HTTP响应的状态码
     * @param byteBuf ByteBuf，包含要发送的响应内容，可能为null
     *                <p>
     *                此方法用于发送HTTP响应。如果byteBuf不为null，则创建一个包含byteBuf内容的FullHttpResponse对象，
     *                否则创建一个仅包含状态码的FullHttpResponse对象。最后，调用sendHttpResponse方法发送响应。
     */
    private void sendResponse(ChannelHandlerContext context, FullHttpRequest request, HttpResponseStatus status, ByteBuf byteBuf) {
        FullHttpResponse response = byteBuf != null
                ? new DefaultFullHttpResponse(HTTP_1_1, status, byteBuf.retainedDuplicate())
                : new DefaultFullHttpResponse(HTTP_1_1, status);
        sendHttpResponse(context, request, response);
    }


    /**
     * 发送HTTP响应
     *
     * @param ctx 通道处理器上下文
     * @param req HTTP请求对象
     * @param res HTTP响应对象
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
        // 如果响应getStatus代码不正常（200） 则生成错误页面
        int statusCode = res.status().code();
        if (statusCode != OK.code() && res.content().readableBytes() == 0) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }

        HttpUtil.setContentLength(res, res.content().readableBytes());
        // 发送响应并在必要时关闭连接
        ChannelFuture channelFuture = ctx.channel().writeAndFlush(res);
        if (!HttpUtil.isKeepAlive(req) || statusCode != OK.code()) {
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }

    }

    /**
     * 获取WebSocket的连接地址
     *
     * @param req HTTP请求对象
     * @return WebSocket的连接地址
     */
    private static String getWebSocketLocation(FullHttpRequest req) {
        String location = req.headers().get(HttpHeaderNames.HOST) + req.uri();
        return "ws://" + location;
    }

}
